解决gunicorn+gevent+django数据库高连接数问题

您所在的位置:网站首页 gunicorn gevent 数据库 解决gunicorn+gevent+django数据库高连接数问题

解决gunicorn+gevent+django数据库高连接数问题

2023-08-29 20:36| 来源: 网络整理| 查看: 265

解决gunicorn+gevent+django数据库高连接数问题 yunsonbai · · 2419 次点击 · · 开始浏览     这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。 第一次,站长亲自招 Gopher 了>>> 原文连接 引言

前段时间分享了一篇如何提高django的并发能力文章,文章的最后结论是采用gunicorn+gthread+django的方式来提高并发能力,该方法简单的说是利用的多线程。 文章也抛出了一个问题:gunicorn+gevent+django+CONN_MAX_AGE会导致数据库连接数飙升,直至占满。如果一定要利用协程的方式启动,该怎么解决这个问题呢?看了一下django源码,找到了问题的根源,写了一下解决办法,下边分享一下。

说明

还是利用上一篇文章如何提高django的并发能力的数据模型,这次以get一条数据为例,由于某些原因(好吧手里没有资源),采用了配置稍低的机器:

服务器: 4核+4G (docker) 压测机: 4核+2G (docker) django: 2.0.8 msyql: 4核+4G(docker) max_connections:1000 max_user_connections:1000 压测方式及命令

压测方式: 压测方式 压测命令: ysab: ysab -n 800 -r 10 -u http://B_ip:8080/test' 备注: 欢迎使用ysab, ysab文档 重现问题 settings DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'ce', 'USER': 'root', 'PASSWORD': '', 'HOST': '192.168.96.95', 'PORT': '3306', 'CONN_MAX_AGE': 600, } } 启动及压测结果

启动: gunicorn --env DJANGO_SETTINGS_MODULE=test_dj21.settings test_dj21.wsgi:application -w 8 -b 0.0.0.0:8080 -k gevent --max-requests 40960 --max-requests-jitter 5120

数据库连接数展示

数据库连接数

qps展示

qps

为什么能达到1000多, 因为一直再查同一条数据。

问题分析与解决 数据库连接数为什么这么高 # django/db/backends/mysql/base.py class DatabaseWrapper(BaseDatabaseWrapper): vendor = 'mysql' . . def get_new_connection(self, conn_params): c = Database.connect(**conn_params) print(id(c)) # 好吧我刻意打印了一下这个id, 每次查询都会重新建立连接用新连接操作 return c

还有一处诡异的代码

class BaseDatabaseWrapper: """Represent a database connection.""" # Mapping of Field objects to their column types. data_types = {} . . def _close(self): if self.connection is not None: with self.wrap_database_errors: print('foo close') # 每次查询完又要调用close return self.connection.close()

经过上边的代码,django关于mysql的部分没有使用连接池,导致每次数据库操作都要新建新的连接。更让我有些蒙的是,按照django的文档CONN_MAX_AGE是为了复用连接,但是为什么每次都要新建连接呢?如此看来并没有复用连接。而且最难受的是一旦我们设置了CONN_MAX_AGE,连接并不会被close掉,而是一直在那占着。 也许是我使用的问题,出现了这个问题。不管如何,最后想了解决办法,请往下看

问题的解决 代码部分 settings代码 DATABASES = { 'default': { 'ENGINE': 'test_dj21.db.backends.mysql', # 好吧核心都在这 'NAME': 'ce', 'USER': 'root', 'PASSWORD': '', 'HOST': '10.10.10.10', 'PORT': '3306', 'CONN_MAX_AGE': 600, } }

test_dj21.db.backends.mysql所在位置

tree base.py import random from django.core.exceptions import ImproperlyConfigured try: import MySQLdb as Database except ImportError as err: raise ImproperlyConfigured( 'Error loading MySQLdb module.\n' 'Did you install mysqlclient?' ) from err from django.db.backends.mysql.base import * from django.db.backends.mysql.base import DatabaseWrapper as _DatabaseWrapper class DatabaseWrapper(_DatabaseWrapper): def get_new_connection(self, conn_params): return ConnectPool.instance(conn_params).get_connection() def _close(self): return None # 假关闭 class ConnectPool(object): def __init__(self, conn_params): self.conn_params = conn_params self.n = 5 self.connects = [] # 未实现单例,实现连接池 @staticmethod def instance(conn_params): if not hasattr(ConnectPool, '_instance'): ConnectPool._instance = ConnectPool(conn_params) return ConnectPool._instance def get_connection(self): c = None if len(self.connects)


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3